#include "PLVertexFactory.h"

using std::set;

PLVertexFactory *PLVertexFactory::mFactory = 0;

/**
  *	Constructs a new PLVertexFactory object
  */
PLVertexFactory::PLVertexFactory( void )
{

}

/**
  *	Destroys the PLVertexFactory object
  */
PLVertexFactory::~PLVertexFactory( void )
{

}

/**
  *	Returns a pointer to the desired PLVertex object. When the PLVertex object
  * already exists in the PLVertexFactory, the pointer to it is returned, otherwise
  * the PLVertex object is constructed first.
  *
  * @param vertexIndex vertex index
  * @param texcoordIndex texcoord index
  * @param textureID unique texture ID
  * @return pointer to the desired PLVertex object
  */
PLVertex *PLVertexFactory::BorrowVertex( PLuint vertexIndex, PLuint texcoordIndex, PLuint textureID )
{
	PLVertex *vertex = new PLVertex( vertexIndex, texcoordIndex, textureID );
	PLVertexNumPair *pair = new PLVertexNumPair( vertex, 1U );

	set<PLVertexNumPair*, PLVertexFactory::PLVertexNumPairPCompare>::const_iterator c_it =
		mUsedVertexes.find( pair );
	
	if (c_it != mUsedVertexes.end())
	{
		delete vertex;
		delete pair;

		(*c_it)->mNum++;

		return (*c_it)->mVertex;
	}

	mUsedVertexes.insert( pair );
	return vertex;
}

/**
  *	Returns the pointer to the one and only PLVertexFactory object
  *
  * @return pointer to the one and only PLVertexFactory object
  */
PLVertexFactory *PLVertexFactory::Get( void )
{
	if (!mFactory)
		mFactory = new PLVertexFactory();

	return mFactory;
}

/**
  *	Returns the minimal usage number of all vertexes
  *
  * @return minimal usage number of all vertexes
  */
PLuint PLVertexFactory::GetMinUsageOfVertex( void ) const
{
	PLuint min_usage = 0xffffffff;

	for (set<PLVertexNumPair*, PLVertexFactory::PLVertexNumPairPCompare>::const_iterator it = mUsedVertexes.begin();
		it != mUsedVertexes.end(); it++)
	{
		if ((*it)->mNum < min_usage)
			min_usage = (*it)->mNum;
	}

	return min_usage;
}

/**
  *	Returns the maximal usage number of all vertexes
  *
  * @return maximal usage number of all vertexes
  */
PLuint PLVertexFactory::GetMaxUsageOfVertex( void ) const
{
	PLuint max_usage = 0;

	for (set<PLVertexNumPair*, PLVertexFactory::PLVertexNumPairPCompare>::const_iterator it = mUsedVertexes.begin();
		it != mUsedVertexes.end(); it++)
	{
		if ((*it)->mNum > max_usage)
		{
			max_usage = (*it)->mNum;
		}
	}

	return max_usage;
}

/**
  *	Returns the number of borrowed vertexes
  *
  * @return number of borrowed vertexes
  */
PLuint PLVertexFactory::GetNumberOfBorrowedVertexes( void ) const
{
	return static_cast<PLuint>( mUsedVertexes.size() );
}

/**
  *	Returns the sum of all vertex usage numbers
  *
  * @return sum of all vertex usage numbers
  */
PLuint PLVertexFactory::GetSumBorrows( void ) const
{
	PLuint sum_borrows = 0;

	for (set<PLVertexNumPair*, PLVertexFactory::PLVertexNumPairPCompare>::const_iterator it = mUsedVertexes.begin();
		it != mUsedVertexes.end(); it++)
	{
		sum_borrows += (*it)->mNum;
	}

	return sum_borrows;
}

/**
  *	Returns true, when a PLVertex object has a usage number of one, false otherwise.
  * The data of the PLVertex object is stored into the given arguments.
  *
  * @param[out] vertexIndex vertex index of the PLVertex object which has a usage of one
  * @param[out] texcoordIndex texcoord index of the PLVertex object which has a usage of one
  * @param[out] textureID texture ID of the PLVertex object which has a usage of one
  * @return true, when a PLVertex object has a usage number of one, false otherwise
  */
bool PLVertexFactory::HasUsageOneForVertex( PLuint *vertexIndex, PLuint *texcoordIndex, PLuint *textureID )
{
	for (set<PLVertexNumPair*, PLVertexFactory::PLVertexNumPairPCompare>::const_iterator it = mUsedVertexes.begin();
		it != mUsedVertexes.end(); it++)
	{
		if ((*it)->mNum == 1)
		{
			*vertexIndex = (*it)->mVertex->GetVertexIndex();
			*texcoordIndex = (*it)->mVertex->GetVertexIndex();
			*textureID = (*it)->mVertex->GetTextureID();

			return true;
		}
	}

	return false;
}

/**
  *	Tells the PLVertexFactory, that the given PLVertex object is not needed anymore.
  * The usage number is decremented by one and the PLVertex object deleted, if it is
  * not used by anyone else.
  *
  * @param vertex pointer to the PLVertex object that is not needed anymore
  */
void PLVertexFactory::ReturnVertex( PLVertex *vertex )
{
	PLVertexNumPair pair( vertex, 1U );

	set<PLVertexNumPair*, PLVertexFactory::PLVertexNumPairPCompare>::const_iterator c_it =
		mUsedVertexes.find( &pair );

	if (c_it != mUsedVertexes.end())
	{
		if ((*c_it)->mNum == 1U)
		{
			// Never needed anymore, delete it
			delete (*c_it)->mVertex;
			delete (*c_it);
			mUsedVertexes.erase( c_it );
		}
		else
		{
			(*c_it)->mNum--;
		}
	}
}

/**
  *	Overloaded ()-operator for the PLVertexNumPairPCompare class.
  * Calls the ()-operator of PLVertex::PLVertexPCompare.
  *
  * @param first const pointer to the first PLVertexNumPair object in the comparison
  * @param second const pointer to the second PLVertexNumPair object in the comparison
  * @return the return value of the call of PLVertex::PLVertexPCompare( first->mVertex, second->mVertex)
  */
bool PLVertexFactory::PLVertexNumPairPCompare::operator()(const PLVertexNumPair *first, const PLVertexNumPair *second) const
{
	PLVertex::PLVertexPCompare compare;
	return compare(first->mVertex, second->mVertex);
}